/**
 * @file 播放页
 * @module pages/play
 * @author 月落 <yueluo.yang@qq.com>
 */

/**
 * @requires config/navbar - 导航配置
 * @requires config/play - 播放相关配置
 * @requires config/topic - 主题配置
 * @requires models/music - 音乐功能
 * @requires models/story - 故事功能
 * @requires models/service - 通用业务
 * @requires libs/router - 路由跳转
 * @requires libs/tools - 工具方法
 * @requires libs/throttle - 工具函数
 * @requires reactivity - 状态管理库
 * @requires mixins/index - mixins
 */
import { play_navbars } from '../../config/navbar';
import { play_mode_icons, play_modes, RECODE_MAX_LIMIT } from '../../config/play';
import { TOPIC, OPEARTION_TYPE, COLLECT_TYPE } from '../../config/topic';
import musicModel from '../../models/music';
import storyModel from '../../models/story';
import serviceModel from '../../models/service';
import router from '../../libs/router';
import tools from '../../libs/tools';
import { throttle } from '../../libs/utils';
import { createPage, effect } from '@minipro/reactivity';
import { common } from '../../mixins/index';

createPage()({
  mixins: [
    common
  ],

  /**
   * @property {object} playParams - 播放参数
   * @property {object} playList - 播放列表
   */
  $data: (ctx) => {
    const { playParams, playList, playRecord } = ctx.$store.state;
    
    return {
      playParams,
      playList,
      playRecord
    }
  },

  /**
   * @property {object} navbars - 图标导航配置
   * @property {array} playModes - 播放模式配置
   * @property {boolean} isPlaying - 正在播放
   * @property {boolean} isCollect - 是否收藏
   * @property {object} audioManager - 音频管理器
   * @property {numer} sliderHeight - 滑块长度
   * @property {number} currentTime - 音频播放进度
   * @property {number} audioProgress - 音频播放百分比
   * @property {number} audioDuration - 音频总时长
   * @property {object} sliderPos - 滑块位置信息
   * @property {boolean} showPlayList - 显示播放列表
   */
  data: {
    navbars: play_navbars,
    playModes: play_mode_icons,
    playMode: play_modes.LOOP,
    isPlaying: false,
    isCollect: false,
    audioManager: null,
    sliderHeight: 0,
    currentTime: 0,
    audioProgress: 0,
    audioDuration: 0,
    sliderPos: {},
    showPlayList: false
  },

  onLoad () {
    // 初始化
    this.init();
  },

  /**
   * @description 初始化函数
   * @returns {void}
   */
  init () { 
    // 初始化音频播放器
    this.initPlayAudio();
    // 获取滑块相关属性
    this.getSliderProperty();
    // 查询收藏状态
    effect(() => {
      const { id } = this.$store.state.playParams;
      this.getCollectStatus(id);
    })
  },

  /**
   * @description 查询收藏状态
   * @param {number} id - 音频ID
   * @returns {void}
   */
  async getCollectStatus (id) {
    const collectType = COLLECT_TYPE.SINGLE;

    const { result: { data } } = await serviceModel.getCollectStatus(collectType, id);

    this.setData({
      ...data
    });
  },

  /**
   * @description 初始化音频播放器
   * @returns {void}
   */
  initPlayAudio () {
    if (this.data.audioManager) return this.data.audioManager.play();
    
    const audioManager = wx.getBackgroundAudioManager();

    // 初始化音频事件监听
    this.initAudioListeners(audioManager);

    const { topic } = this.data;

    switch (topic) {
      case TOPIC.MUSIC:
        // 播放音乐
        this.initMusicAudio(audioManager);
        break;
      case TOPIC.STORY:
        // 播放故事
        this.initStoryAudio(audioManager);
        break;
      default:
        break;
    }
  },

  /**
   * @description 获取滑块长度
   * @returns {void}
   */
  getSliderProperty () {
    const query = wx.createSelectorQuery();
    query.select('#J-slider').boundingClientRect();
    query.exec((res) => {
      const { width } = res[0];
      this.setData({
        sliderHeight: width 
      });
    });
  },

  /**
   * @description 播放音乐
   * @param {object} audioManager - 音频播放器
   * @returns {void} 
   */
  async initMusicAudio (audioManager) {
    const { id, title, author, imgUrl } = this.data.playParams;

    const { result } = await musicModel.getMusicUrl(id),
          { code, message, data: { url } } = result; 

    if (code === 0 && url) {
     return this.playAudio({
      audioManager,
      title,
      author,
      imgUrl,
      url
     });
    } 

    console.log(code, message, url);
    const { total } = this.data.playList;

    if (total) {
      // 1. 清除旧音频实例
      this.clearPlayManager();
      // 2. 播放下一段音频
      this.playNextAudio();
      return;
    }

    router.goBack();
  },

  /**
   * @description 播放音乐
   * @param {object} audioManager - 音频播放器
   * @returns {void} 
   */
  async initStoryAudio (audioManager) {
    const { id, title, imgUrl } = this.data.playParams;
    const { result } = await storyModel.getAudioUrl(id),
          { code, message, data: { url } } = result; 
    
    if (code === 0 && url) {
      return this.playAudio({
        audioManager,
        title,
        author: '月落',
        imgUrl,
        url
      });
    } 

    console.log(code, message, url);
    router.goBack();
  },

    /**
   * @description 播放音频
   * @param {object} audioManager - 音频播放器
   * @param {string} title - 名称
   * @param {string} author - 作者
   * @param {string} imgUrl - 图片地址 
   * @param {string} url - 播放地址
   * @returns {void}
   */
  playAudio ({ audioManager, title, author, imgUrl, url }) {
    console.log(title, author, imgUrl, url);

    audioManager.title = title;
    audioManager.epname = title;
    audioManager.singer = author;
    audioManager.coverImgUrl = imgUrl;
    audioManager.src = url;

    // 循环换取音频总长度（获取音频有延迟）
    let getDurationTimer = setInterval(() => {
      const totalTime = audioManager.duration;

      if (typeof totalTime === 'number' && totalTime) {
        clearInterval(getDurationTimer);

        // 1. 设置音频长度
        this.setData({
          audioDuration: Math.round(totalTime)
        });
        // 2. 缓存播放记录
        this.recordAudio();
      }
    }, 300)

    this.setData({
      audioManager
    });
  },

  /**
   * @description 初始化音频监听事件
   * @param {object} audioManager - 音频管理器
   * @returns {void} 
   */
  initAudioListeners (audioManager) {
    audioManager.onPlay(() => {
      console.log('[audio]：', 'onPlay');
      
      this.setData({
        isPlaying: true
      });
    });

    ['onPause', 'onStop', 'onError', 'onEnded'].forEach(method => {
      audioManager && audioManager[method]((reason) => {
        console.log('[audio]：', method, reason && '');

        this.setData({
          isPlaying: false
        });

        if (method === 'onEnded') {
          // 1. 清除旧音频实例
          this.clearPlayManager();
          // 2. 播放下一段音频
          this.playNextAudio();
        }
      })
    });

    // 音频播放进度更新
    audioManager.onTimeUpdate(() => {
      const { audioManager, audioDuration, currentTime } = this.data;
      
      if (audioManager && audioDuration) {
        const current = Math.floor(audioManager.currentTime);

        if (current === currentTime) return;
        if (this.isSliderChange) return;
        
        this.setData({
          currentTime: current,
          audioProgress: Math.floor((current / audioDuration) * 100)
        });
      }
    });
  },

  /**
   * @description 缓存播放记录
   * @returns {void}
   */
  recordAudio () {
    const { playParams, playRecord } = this.data,
          { topic, type } = this.$store.state,
          total = playRecord.length;

    // 移除已经存在数据
    const recordIdx = playRecord.findIndex(item => item.id === playParams.id);
    if (!!~recordIdx) {
      playRecord.splice(recordIdx, 1);
    }

    // 添加新数据
    playRecord.push(
      Object.assign({ topic, type }, playParams)
    );

    // 限制缓存音频数量
    if (total > RECODE_MAX_LIMIT) {
      playRecord.splice(0, 1);
    }

    // 提交数据
    this.$store.commit('changeState', {
      playRecord
    });
  },

  /**
   * @description 清除旧音频实例
   * @returns {void}
   */
  clearPlayManager () {
    this.setData({
      currentTime: 0,
      audioManager: null
    });
  },

  /**
   * @description 播放下一段音频
   * @returns {void}
   */
  playNextAudio () {
    const { playMode } = this.data;

    /**
     * @descriptions 延迟播放音频
     * @returns {void}
     */
    const restartPlayAudio = () => {
      setTimeout(() => {
        // 重新播放音频
        this.initPlayAudio();
      }, 500);
    }

    switch (playMode) {
      case 'single':
        // 延迟播放音频
        restartPlayAudio();
        break;
      case 'loop':
        // 处理循环播放
        processLoopAudio.call(this);
        break;
    }

    /**
     * @description 处理循环播放
     * @returns {void}
     */
    function processLoopAudio () {
      let { total, curIdx, lists } = this.data.playList;

      if (!total) return;

      curIdx < total - 1 ? curIdx++ : (curIdx = 0);

      // 提交数据
      this.$store.commit('changeState', {
        playList: {
          ...this.data.playList,
          curIdx: curIdx
        },
        playParams: lists[curIdx]
      });

      // 延迟播放音频
      restartPlayAudio();
    }
  },

  /**
   * @description 导航栏点击事件处理
   * @param {object} e - 事件源
   * @returns {void} 
   */
  onIconBtnClick: throttle(function (e) {
    const { action } = e.currentTarget.dataset;

    switch (action) {
      case 'back':
        router.goBack();
        break;
      case 'collect':
        this.collectSingle();
        break;
      default:
        console.log('no matching entries.');
        break;
    }
  }, 300, false),

  /**
   * @description 收藏音频
   * @returns {void}
   */
  async collectSingle () {
    const { isCollect, playParams } = this.data,
          { topic, type } = this.$store.state;

    const operation = isCollect ? OPEARTION_TYPE.DELETE : OPEARTION_TYPE.ADD,
          collectType = COLLECT_TYPE.SINGLE;

    const { result: { code, message } } = await serviceModel.collect(collectType, operation, Object.assign({
      topic,
      type
    }, playParams));

    if (code === 0) {
      this.setData({
        isCollect: !isCollect
      });
      return;
    }
    
    console.log(message);
  },

  /**
   * @description 播放、暂停音频
   * @returns {void}
   */
  onAudioClick () { 
    const { isPlaying, audioManager } = this.data;
    audioManager && audioManager[isPlaying ? 'pause' : 'play']();
  },

  /**
   * @description 音频进度控制
   * @param {object} e - 事件源
   * @returns {void} 
   */
  onProgressControl: throttle(function (e) {
    const { action } = e.currentTarget.dataset,
          { audioManager, currentTime, audioDuration } = this.data;

    switch (action) {
      case 'down':
        // 后退15s
        let prevTime = currentTime - 15;
        if (prevTime < 0) {
          prevTime = 0
        };
        audioManager.seek(prevTime);
        break;
      case 'up':
        // 前进15s
        let nextTime = currentTime + 15;
        if (nextTime >= audioDuration) {
          nextTime = audioDuration;
        };
        audioManager.seek(nextTime);
        break;
      default:
        console.log('no matching entries.');
        break;
    }
  }, 200, false),

  /**
   * @description 播放模式控制
   * @param {object} e - 事件源
   * @returns {void} 
   */
  onPlayModeControl (e) {
    const { action } = tools.getEventDetail(e);

    switch (action) {
      case 'timing':
        console.log('定时播放');
        break;
      case 'single':
      case 'loop':
        this.setData({
          playMode: action
        });
        break;
      case 'playlist':
        // 打开播放列表
        this.setData({
          showPlayList: true
        });
        break;
      case 'close':
        // 关闭播放列表
        this.setData({
          showPlayList: false
        });
        break;
      default:
        console.log('no matching entries.');
        break;
    }
  },

  // 延迟改变滑块进度
  delayTime: null,
  // 是否是手动拖动滑块
  isSliderChange: false,

  /**
   * @description 滑动滑块
   * @param {object} e - 事件源
   * @returns {void}   
   */
  onSliderChange (e) {
    if (this.delayTime) {
      clearTimeout(this.delayTime);
    };

    // 如果非触摸状态
    if (!this.isSliderChange) return;

    this.delayTime = setTimeout(() => {
      const { sliderHeight, audioDuration, audioManager } = this.data,
            { x } = e.detail;

      const curTime = audioDuration * (x / sliderHeight).toFixed(2);

      audioManager && audioManager.seek(curTime);

      this.isSliderChange = false;
    }, 300)
  },

  /**
   * @description 滑块首次触摸
   * @returns {void}   
   */
  onSliderTouchMove () {
    this.isSliderChange = true;
  },

  /**
   * @description 列表点击事件
   * @param {object} e - 事件源
   * @returns {void} 
   */
  onPlayListClick: throttle(function (e) {
    const { data: { index, id: newId } } = tools.getEventDetail(e);
    const { id } = this.data.playParams;

    if (id === newId) {
      console.log('禁止切换当前音乐.');
      return;
    }

    const { lists } = this.data.playList;

    // 提交数据
    this.$store.commit('changeState', {
      playList: {
        ...this.data.playList,
        curIdx: index
      },
      playParams: lists[index]
    });
    
    // 隐藏播放列表
    this.setData({
      showPlayList: false
    });
    // 清除音频实例
    this.clearPlayManager();
    // 播放选中音频
    this.initPlayAudio();
  }, 300, false),
})